home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Sample Code / AppsToGo / DTS.Draw / Window.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  49.8 KB  |  1,665 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* This file contains the code for the document procedure pointers for the main DTS.Draw
  20. ** document.  DTS.Draw currently only supports one type of file-based documents,
  21. ** type 'DDOC'. */
  22.  
  23. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  24.  
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29.  
  30.  
  31. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  32. #include "App.defs.h"        /* Get various application definitions.            */
  33. #include "App.protos.h"        /* Get the prototypes for application.            */
  34.  
  35. #ifndef __ERRORS__
  36. #include <Errors.h>
  37. #endif
  38.  
  39. #ifndef __FONTS__
  40. #include <Fonts.h>
  41. #endif
  42.  
  43. #ifndef __RESOURCES__
  44. #include <Resources.h>
  45. #endif
  46.  
  47. #ifndef __TOOLUTILS__
  48. #include <ToolUtils.h>
  49. #endif
  50.  
  51. #ifndef __TREEOBJ2__
  52. #include "TreeObj2.h"
  53. #endif
  54.  
  55. #ifndef __UTILITIES__
  56. #include "Utilities.h"
  57. #endif
  58.  
  59.  
  60.  
  61. /*****************************************************************************/
  62.  
  63.  
  64.  
  65. typedef struct {
  66.     TreeObjHndl    root;
  67.     short        cnum;
  68. } LayerDrawInfo;
  69.  
  70. Boolean            gNoDefaultDocument = false;
  71.                     /* Set to true if app should boot with no default document. */
  72.                     /* This tells DTS.Lib..framework what you want. */
  73.  
  74. OSType            gAppWindowType = kDocFileType;    /* Main document type. */
  75. long            gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  76. long            gToolPaletteMenuFlags;            /* Remember what items are enabled at startup. */
  77.  
  78. short            gMinVersion = kMinVersion;    /* Minimum document version app can support. */
  79. short            gMaxVersion = kMaxVersion;    /* Maximum document version app can support. */
  80.                                             /* More informing DTS.Lib..framework. */
  81.  
  82. extern short    gPrintPage;                    /* Non-zero means we are printing. */
  83.                                             /* DTS.Lib..framework global. */
  84.  
  85. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  86. extern CursPtr        gCursorPtr;                /* to know about these things. */
  87.                                             /* Above are DTS.Lib..framework globals. */
  88.  
  89. static void        AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click);
  90. static void        SlideSelection(FileRecHndl frHndl, ClickInfo *click);
  91. static OSErr    WindowLayerProc(LayerObj theLayer, short message);
  92. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  93. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  94. static void        DrawPageGrid(void);
  95.  
  96. #define kDataAreaPadding 0
  97.  
  98.  
  99. /*****************************************************************************/
  100. /*****************************************************************************/
  101. /*****************************************************************************/
  102.  
  103. #ifdef applec
  104. #pragma segment DTSDrawSeg2
  105. #endif
  106.  
  107. /*****************************************************************************/
  108. /*****************************************************************************/
  109.  
  110.  
  111.  
  112. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  113.  
  114. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  115. ** You are passed an empty region.  You are supposed to add any custom frame
  116. ** parts that this document uses.  Typically there are no frame portions, as
  117. ** they are accounted for in other ways.  The scrollbars and grow icon will
  118. ** automatically be contributed to the calculation of the frame region.
  119. ** If you use sidebars, these are also added in automatically.  This is only
  120. ** used if the frame region is more complicated than can automatically be
  121. ** handled.  So, almost always, you will simply leave the region empty. */
  122.  
  123. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  124. {
  125. #ifndef __MWERKS__
  126. #pragma unused (frHndl, window, rgn)
  127. #endif
  128. }
  129.  
  130.  
  131.  
  132. /*****************************************************************************/
  133.  
  134.  
  135.  
  136. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  137.  
  138. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  139. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  140. ** further process the click. */
  141.  
  142. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  143. {
  144. #ifndef __MWERKS__
  145. #pragma unused (firstClick)
  146. #endif
  147.  
  148.     FileRecHndl        frHndl;
  149.     ControlHandle    ctl;
  150.     short            dblClick, shiftMod;
  151.     Point            contOrg;
  152.     Boolean            selected;
  153.     TreeObjHndl        hitObj, root;
  154.     ClickInfo        click;
  155.  
  156.     SetPort(window);
  157.  
  158.     if (IsCtlEvent(window, event, &ctl, &dblClick)) return;
  159.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  160.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  161.         ** controls in the content besides the document scrollbars. */
  162.  
  163.     frHndl = (FileRecHndl)GetWRefCon(window);
  164.     if ((*frHndl)->fileState.readOnly) return;
  165.         /* Don't allow changes if read-only. */
  166.  
  167.     /* If none of the above resolved why we are here, then the click was in the
  168.     ** actual drawing area of the content, and we have some serious work to do. */
  169.  
  170.     GetContentOrigin(window, &contOrg);
  171.     SetOrigin(contOrg.h, contOrg.v);
  172.         /* This sets the origin of the document area of the content to the position
  173.         ** indicated by the document scrollbars. */
  174.  
  175.     click.localEvent = *event;
  176.     GlobalToLocal(&click.localEvent.where);
  177.         /* Who wants to work in global coordinates, anyway... */
  178.  
  179.     if (!GetTool()) {            /* If no specific tool (arrow tool)... */
  180.  
  181.         click.message = HITTESTGRABBER;
  182.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  183.             /* See if the user clicked on a grabber for a selected object. */
  184.  
  185.         if (!hitObj) {        /* If user didn't click on a grabber, see if they hit an object. */
  186.             click.message = HITTESTOBJ;
  187.             hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  188.         }
  189.  
  190.         shiftMod = click.localEvent.modifiers & shiftKey;
  191.             /* Find out if the user was holding down the shift key. */
  192.  
  193.         if (click.message == HITTESTGRABBER)
  194.             shiftMod = 0;
  195.                 /* Pretent that the shift key isn't held down if they are on a grabber.  If
  196.                 ** they are on a grabber, you want to do a constrain with the shift key. */
  197.  
  198.         if (hitObj) {        /* User hit either a grabber or the object itself. */
  199.  
  200.             selected = mDerefCommon(hitObj)->selected;
  201.                 /* See if the object hit, independent of type, was selected. */
  202.  
  203.             if ((!selected) && (!shiftMod))        /* The object isn't selected, and */
  204.                 DoTreeSelect(root, SELECTOFF);    /* the shift key isn't held down, */
  205.                                                 /* so deselect all other objects. */
  206.  
  207.             click.message = CLICKSELECT;
  208.             if ((!selected) || (shiftMod))
  209.                 DoTreeObjMethodClipped(hitObj, CLICKMESSAGE, (long)&click);
  210.                     /* If the object isn't selected, or if the shift key is down, change
  211.                     ** the select state of the object that was clicked on. */
  212.  
  213.             click.localEvent.modifiers ^= shiftMod;
  214.                 /* Turn off the shift modifier, which makes the rest of the
  215.                 ** handling of the object easier. */
  216.  
  217.             if (mDerefCommon(hitObj)->selected) {    /* If object selected, do something with it. */
  218.                 if (click.grabber == -1)
  219.                     SlideSelection(frHndl, &click);
  220.                 else
  221.                     AddOrSizeObj(frHndl, hitObj, &click);
  222.             }            /* The click could have been used to shift-click deselect an object,
  223.                         ** so only do something with the object if it is selected.  We  either
  224.                         ** resize the object (if a grabber was clicked on) or slide the selected
  225.                         ** objects (if an object was clicked on). */
  226.  
  227.         }
  228.         else {                                    /* No object was clicked on... */
  229.  
  230.             if (!shiftMod)                            /* If not a shift-click, */
  231.                 DoTreeSelect(root, SELECTOFF);        /* deselect everything.  */
  232.  
  233.             AddOrSizeObj(frHndl, hitObj, &click);    /* Do extend select. */
  234.         }
  235.     }
  236.     else {
  237.         click.message = HITTESTGRABBER;
  238.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  239.             /* See if the user clicked on a grabber for a selected object. */
  240.         AddOrSizeObj(frHndl, hitObj, &click);
  241.             /* No object clicked on, so add an object. */
  242.     }
  243. }
  244.  
  245.  
  246.  
  247. /*****************************************************************************/
  248.  
  249.  
  250.  
  251. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  252.  
  253. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  254. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  255. **
  256. ** 1) If you handle the key, return(true).  This completes the key handling.
  257. ** 2) If you don't handle the key, you return false.  However, there are two
  258. **    situations for not handling the key:
  259. **      a) You want someone else to.
  260. **      b) You want nobody else to look at the key.
  261. **    This is what the boolean passThrough is for.  If you wish the next window
  262. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  263. **    is already initialized to false, which is the common case, so you only have
  264. **    to worry about setting it true.
  265. **
  266. ** If you have a window that never processes keys and always passes them through,
  267. ** just set the contentKeyProc to nil.  This will indicate to the application
  268. ** framework that all keys should be passed through this window.  DTS.Draw has
  269. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  270. ** to document windows. */
  271.  
  272. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  273. {
  274. #ifndef __MWERKS__
  275. #pragma unused (passThrough)
  276. #endif
  277.  
  278.     char        key;
  279.     FileRecHndl    frHndl;
  280.  
  281.     key = event->message & charCodeMask;
  282.     if (key != 8) return(false);
  283.         /* The only key we handle is the delete key.  leave for all the rest. */
  284.  
  285.     SetPort(window);
  286.  
  287.     frHndl = (FileRecHndl)GetWRefCon(window);
  288.     if ((*frHndl)->fileState.readOnly) return(false);
  289.         /* Don't allow changes if read-only. */
  290.  
  291.     DoDelete(frHndl);
  292.  
  293.     return(true);
  294. }
  295.  
  296.  
  297.  
  298. /*****************************************************************************/
  299.  
  300.  
  301.  
  302. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  303.  
  304. /* Draw application specific content (Called by DoDrawFrame).
  305. **
  306. ** If your application has any custom frame areas, or if it uses sidebars,
  307. ** this is the function that you would put the frame drawing code.  The
  308. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  309. ** Just do the sidebar and custom areas here. */
  310.  
  311. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  312. {
  313.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  314.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  315.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  316.  
  317.     BeginFrame(window);
  318.     DoDrawControls(window, activate);
  319.     EndFrame(window);
  320. }
  321.  
  322.  
  323.  
  324. /*****************************************************************************/
  325.  
  326.  
  327.  
  328. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  329.  
  330. /* Frees up any application-specific memory in the document.  This is called by
  331. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  332. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  333. **
  334. ** The document may have a bunch of handles off the main handle of the document.
  335. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  336. ** the ram for the main handle of the document, so release everything else
  337. ** here, or you will have a memory leak.
  338. **
  339. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  340. ** hierarchical document (see TreeObj package). */
  341.  
  342. OSErr    FreeDocument(FileRecHndl frHndl)
  343. {
  344.     return(DefaultFreeDocument(frHndl));
  345. }
  346.  
  347.  
  348.  
  349. /*****************************************************************************/
  350.  
  351.  
  352.  
  353. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  354.  
  355. /* Any additional window disposal tasks can be handled here. */
  356.  
  357. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  358. {
  359. #ifndef __MWERKS__
  360. #pragma unused (window)
  361. #endif
  362.  
  363.     WindowPtr    ww;
  364.     FileRecHndl    ff;
  365.  
  366.     if ((*frHndl)->fileState.sfType == kDocFileType) {
  367.         ManageDragHandlers(window, false);
  368.         for (ww = nil; (ww = GetNextWindow(ww, 0)) != nil;) {
  369.             ff = (FileRecHndl)GetWRefCon(ww);
  370.             if ((*ff)->fileState.sfType == kViewHierFileType) {
  371.                 if ((*frHndl)->d.doc.root == (*ff)->d.doc.root) {
  372.                     DisposeOneWindow(ww, kClose);
  373.                     ww = nil;
  374.                 }
  375.             }
  376.         }
  377.     }
  378.  
  379.     return(noErr);
  380. }
  381.  
  382.  
  383.  
  384. /*****************************************************************************/
  385.  
  386.  
  387.  
  388. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  389.  
  390. /* Image the document into the current port.
  391. **
  392. ** The only thing tricky about this function is that it needs to key off of
  393. ** the global variable gPrintPage.  gPrintPage is the current page that is
  394. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  395. **
  396. ** For when printing:
  397. **
  398. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  399. ** the page there are no more pages, you should set gPrintPage to 0.  This
  400. ** indicates to the print loop that the end of the document has been reached.
  401. ** Even if the user indicated in the job dialog to print more pages, setting
  402. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  403. ** because the print loop can't know when printing is done.  The imaging procedure
  404. ** is the logical one to state when everything has been imaged. */
  405.  
  406. OSErr    ImageDocument(FileRecHndl frHndl)
  407. {
  408.     LayerDrawInfo    drawInfo;
  409.     LayerObj        wlyr, blyr;
  410.  
  411.     drawInfo.root = (*frHndl)->d.doc.root;
  412.         /* If there isn't a background layer, then all objects are drawn into
  413.         ** the work layer, and therefore we don't need to fill in the cnum field
  414.         ** of the drawInfo.  The WorkLayerProc will notice that there isn't a
  415.         ** layer behind it and will automatically do the right thing. */
  416.  
  417.     if (gPrintPage) {
  418.         DoTreeDraw(drawInfo.root, DRAWOBJ);        /* Draw the page. */
  419.         gPrintPage = 0;                            /* We only support one page in this sample. */
  420.         return(noErr);
  421.     }
  422.  
  423.     NewLayer(&wlyr, nil, WindowLayerProc, (*frHndl)->fileState.window, 0, (long)&drawInfo);
  424.         /* Create a layer object for the window. */
  425.  
  426.     NewLayer(&blyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  427.         /* Create a background layer for drawing of all of the objects. */
  428.  
  429.     InvalLayer(wlyr, GetEffectiveDstRect(wlyr), false);
  430.         /* We want to draw the entire contents. */
  431.  
  432.     UpdateLayer(wlyr);
  433.         /* Update what's invalid, which is everything.  All the objects are drawn into
  434.         ** the background layer, and then the background layer is transferred into the
  435.         ** window. */
  436.  
  437.     DisposeThisAndBelowLayers(wlyr);
  438.         /* Clean up what we created. */
  439.  
  440.     return(noErr);
  441. }
  442.  
  443.  
  444.  
  445. /*****************************************************************************/
  446.  
  447.  
  448.  
  449. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  450.  
  451. /* This function does the remaining window initialization.
  452. **
  453. ** There may be additional content initialization for the window.  At this point,
  454. ** you have a window, but it is currently invisible.  If you return noErr, then
  455. ** the window will be set to the state indicated for that window.  Why this function?
  456. ** You may wish to add controls to the content of the window.  You may have a
  457. ** TextEdit record in the content.  All of these sort of things can't be created
  458. ** until there is a window to contain them.  First a document is read in, and then
  459. ** if the document creation succeeds, a window is created for that document.
  460. ** At this point we have a document, and we are on our way to having a window.
  461. ** All that remains is any additional content initialization.  Do it, return
  462. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  463. ** and the incomplete window will be disposed of. */
  464.  
  465. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  466. {
  467.     OSErr        err;
  468.     Rect        rct;
  469.     TreeObjHndl    root;
  470.  
  471.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  472.  
  473.     rct = GetDataArea(root = (*frHndl)->d.doc.root);
  474.     SetDataArea(root, rct.right, rct.bottom);
  475.     ManageDragHandlers(window, true);
  476.  
  477.     return(err);
  478. }
  479.  
  480.  
  481.  
  482. /*****************************************************************************/
  483.  
  484.  
  485.  
  486. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  487.  
  488. /* The code below assumes that you are using the hierarchical document package.
  489. ** If you are, the entire hierarchical document is read in with just these two
  490. ** calls.  If you don't use it, you are on your own.  See DTS.Chat for an
  491. ** example of an application that uses the DTS.framework without the hierarchical
  492. ** document package. */
  493.  
  494. OSErr    ReadDocument(FileRecHndl frHndl)
  495. {
  496.     OSErr        err;
  497.     TreeObjHndl    root;
  498.  
  499.     err = DefaultReadDocument(frHndl);
  500.     root = (*frHndl)->d.doc.root;
  501.     mDerefRoot(root)->numSelected = 0;        /* User may have saved when objects were selected. */
  502.     if (!err)
  503.         DefaultReadDocumentFixup(frHndl);
  504.  
  505.     return(err);
  506. }
  507.  
  508.  
  509.  
  510. /*****************************************************************************/
  511.  
  512.  
  513.  
  514. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  515.  
  516. /* Resize application specific content (Called by ResizeWindow).
  517. **
  518. ** This gets called when a user does a zoom or window resizing operation.
  519. ** It is possible that things in the content need to be resized in conjunction
  520. ** with the resizing of the window. */
  521.  
  522. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  523. {
  524. #ifndef __MWERKS__
  525. #pragma unused (window, oldh, oldv)
  526. #endif
  527.  
  528.     /* See DTS.Chat for a sample usage of this function. */
  529. }
  530.  
  531.  
  532.  
  533. /*****************************************************************************/
  534.  
  535.  
  536.  
  537. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  538.  
  539. /* Scroll application specific frame (Called by DoScrollFrame).
  540. **
  541. ** Some applications may need to scroll the "frame" of the document along
  542. ** with the document contents.  This is common for applications with rulers,
  543. ** or other similar sidebar items. */
  544.  
  545. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  546. {
  547. #ifndef __MWERKS__
  548. #pragma unused (frHndl, window, dh, dv)
  549. #endif
  550. }
  551.  
  552.  
  553.  
  554. /*****************************************************************************/
  555.  
  556.  
  557.  
  558. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  559.  
  560. /* This gets called just prior to and just after an undo/redo operation is done. */
  561.  
  562. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  563. {
  564.     WindowPtr    window;
  565.     TreeObjHndl    root;
  566.     Rect        rct;
  567.  
  568.     window = (*frHndl)->fileState.window;
  569.     root   = (*frHndl)->d.doc.root;
  570.  
  571.     if (!afterUndo)                            /* Before an undo operation we deselect everything. */
  572.         DoTreeSelect(root, SELECTOFF);        /* Only what was undone should end up selected. */
  573.  
  574.     if (afterUndo) {
  575.         rct = GetDataArea(root);
  576.         SetDataArea(root, rct.right, rct.bottom);
  577.         SetContentOrigin(window, contOrg.h, contOrg.v);
  578.             /* Undo may have a different document origin than where the user was.
  579.             ** Scroll the document back to where the edit we are undoing took place. */
  580.  
  581.         BeginContent(window);            /* Redraw the document to display */
  582.         DoImageDocument(frHndl);        /* the changes due to undo. */
  583.         EndContent(window);
  584.     }
  585.  
  586.     DoSetCursor(nil);
  587. }
  588.  
  589.  
  590.  
  591. /*****************************************************************************/
  592.  
  593.  
  594.  
  595. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  596.  
  597. /* This function is where you adjust the cursor to reflect the location in the
  598. ** document or window.  You have the additional input of gCursorRgn to deal
  599. ** with.  The way that the cursor handling works is as follows:
  600. ** 1) The application calls DoWindowCursor().
  601. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  602. **    It looks at the document's windowCursorProc and checks to see if the document
  603. **    has one.  If the document doesn't have one, then it assumes that that window
  604. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  605. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  606. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  607. **    is set to an arrow (for the non-document area of the screen).
  608. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  609. **    job is as follows:
  610. **    a) If the cursor is over a position that is determined by the window, then
  611. **       the proc removes other areas from gCursorRgn.  Note that it should not
  612. **       simply set the area to what it "thinks" is the correct area.  This window
  613. **       may not be the front-most.  Other windows will have already been subtracted
  614. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  615. **       and should be passed to WaitNextEvent calls in the application (already the case
  616. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  617. **       You should also return true, as the cursor has been determined.
  618. **    b) If the cursor is not over a position for this window, then you should
  619. **       return.  You will either pass back true or false.  If you don't wish
  620. **       windows behind this window to have a shot at cursor determination, then
  621. **       return true.  This states that the cursor is "determined".  It is, in the
  622. **       sense that no further determination will occur.  If you return false, then
  623. **       other windows get a shot at determining the cursor.
  624. **
  625. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  626. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  627. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  628. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  629. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  630. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  631. ** to the return result of DoSetResCursor(), and you will be set. */
  632.  
  633. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  634. {
  635. #ifndef __MWERKS__
  636. #pragma unused (frHndl)
  637. #endif
  638.  
  639.     RgnHandle    frameRgn, contRgn;
  640.  
  641.     if (GetTool()) {
  642.         frameRgn = DoCalcFrameRgn(window);
  643.         contRgn  = NewRgn();
  644.         DiffRgn(((WindowPeek)window)->contRgn, frameRgn, contRgn);
  645.         DisposeRgn(frameRgn);
  646.         if (PtInRgn(globalPt, contRgn)) {
  647.             gCursorPtr = DoSetResCursor(addObjCursor);
  648.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  649.             DisposeRgn(contRgn);
  650.             return(true);
  651.         }
  652.         DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  653.         SectRgn(gCursorRgn, contRgn, gCursorRgn);
  654.         DisposeRgn(contRgn);
  655.     }
  656.  
  657.     SetCursor(gCursorPtr = &qd.arrow);
  658.     return(true);
  659. }
  660.  
  661.  
  662.  
  663. /*****************************************************************************/
  664.  
  665.  
  666.  
  667. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  668.  
  669. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  670. ** to give the application a chance to do any additional tasks related to
  671. ** a window closing.  DTS.Chat doesn't have anything else extra to do. */
  672.  
  673. void    WindowGoneFixup(WindowPtr window)
  674. {
  675. #ifndef __MWERKS__
  676. #pragma unused (window)
  677. #endif
  678. }
  679.  
  680.  
  681.  
  682. /*****************************************************************************/
  683.  
  684.  
  685.  
  686. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  687.  
  688. /* The reverse function of ReadDocument. */
  689.  
  690. OSErr    WriteDocument(FileRecHndl frHndl)
  691. {
  692.     return(DefaultWriteDocument(frHndl));
  693. }
  694.  
  695.  
  696.  
  697. /*****************************************************************************/
  698.  
  699.  
  700.  
  701. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  702.  
  703. OSErr    DoOpenApplication(void)
  704. {
  705.     MenuHandle    menu;
  706.  
  707.     menu = GetMenuHandle(mToolPalette);
  708.     if (menu)
  709.         gToolPaletteMenuFlags = (*menu)->enableFlags;
  710.  
  711.     return(noErr);
  712. }
  713.  
  714.  
  715.  
  716. /*****************************************************************************/
  717.  
  718.  
  719.  
  720. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  721.  
  722. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  723. {
  724.     Boolean        redrawMenuBar;
  725.     MenuHandle    menu;
  726.  
  727.     redrawMenuBar = false;
  728.  
  729.     switch (menuID) {
  730.         case mFile:
  731.             redrawMenuBar = DoAdjustFileMenu(window);
  732.             break;
  733.         case mEdit:
  734.             redrawMenuBar = DoAdjustEditMenu(window);
  735.             break;
  736.         case mArrange:
  737.             redrawMenuBar = DoAdjustArrangeMenu(window);
  738.             break;
  739.         case mToolPalette:
  740.             menu = GetMenuHandle(menuID);
  741.             if (menu)
  742.                 (*menu)->enableFlags = gToolPaletteMenuFlags;
  743.             break;
  744.         case mOther:
  745.             redrawMenuBar = DoAdjustOtherMenu(window);
  746.             break;
  747.         default:
  748.             menu = GetMenuHandle(menuID);
  749.             if (menu)
  750.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  751.             break;
  752.     }
  753.  
  754.     return(redrawMenuBar);
  755. }
  756.  
  757.  
  758.  
  759. /*****************************************************************************/
  760.  
  761.  
  762.  
  763. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  764.  
  765. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  766. {
  767. #ifndef __MWERKS__
  768. #pragma unused (window)
  769. #endif
  770.  
  771.     return(DoMenuCommand(menuID, menuItem));
  772. }
  773.  
  774.  
  775.  
  776. /*****************************************************************************/
  777. /*****************************************************************************/
  778. /*****************************************************************************/
  779.  
  780.  
  781.  
  782. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  783. {
  784.     OSErr        err;
  785.     TreeObjHndl    root;
  786.  
  787.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  788.         /* Create a document and root object to copy the file data into. */
  789.  
  790.     if (!err)
  791.         err = CopyChildren((*oldFrHndl)->d.doc.root, (**newFrHndl)->d.doc.root);
  792.             /* Copy the hierarchical data into the new file. */
  793.  
  794.     if (!err) {
  795.         root = (**newFrHndl)->d.doc.root;
  796.         DoFTreeMethod(root, UNDOMESSAGE, UNDOFROMDOC);
  797.         mDerefRoot(root)->numSelected = 0;
  798.     }        /* We have to be careful to use methods that don't try to draw
  799.             ** anything, as we don't have a window to draw into yet. */
  800.  
  801.     return(err);
  802. }
  803.  
  804.  
  805.  
  806. /*****************************************************************************/
  807.  
  808.  
  809.  
  810. /* This is called when a key event occurs and it is determined that it isn't
  811. ** a menu key. */
  812.  
  813. void    DoDelete(FileRecHndl frHndl)
  814. {
  815.     WindowPtr    window;
  816.     TreeObjHndl    root;
  817.     short        cnum;
  818.     Boolean        didDelete;
  819.  
  820.     root = (*frHndl)->d.doc.root;
  821.         /* All of the selected objects are children of the root, so all we have to do
  822.         ** is walk the children of the root looking for selected objects.  If the
  823.         ** document has grouping objects, which have sub-children, only the group
  824.         ** object is selected. */
  825.  
  826.     for (didDelete = false, cnum = (*root)->numChildren; cnum;) {
  827.         if (mDerefCommon(GetChildHndl(root, --cnum))->selected) {
  828.             if (!didDelete)
  829.                 NewDocumentUndo(frHndl);
  830.                     /* Make sure this delete event is posted in a separate undo from
  831.                     ** a possible previous delete event. */
  832.             DisposeChild(DELETE_EDIT, root, cnum);
  833.                 /* Post the delete of the child into the undo hierarchy. */
  834.             didDelete = true;
  835.                 /* Yes, we did change the document. */
  836.         }
  837.     }
  838.  
  839.     if (didDelete) {                            /* If something got deleted... */
  840.         window = (*frHndl)->fileState.window;
  841.         SetWindowDirty(window);                    /* Flag the document as dirty. */
  842.         BeginContent(window);                    /* Redraw the contents. */
  843.         DoImageDocument(frHndl);
  844.         EndContent(window);
  845.     }
  846. }
  847.  
  848.  
  849.  
  850. /*****************************************************************************/
  851.  
  852.  
  853.  
  854. void    DoArrange(FileRecHndl frHndl, short menuItem)
  855. {
  856.     TreeObjHndl    root;
  857.     short        cnum, cdir, cend;
  858.     WindowPtr    window;
  859.  
  860.     if (menuItem >= kGroup) {
  861.         switch (menuItem) {
  862.             case kGroup:
  863.                 DoGroup(frHndl);
  864.                 break;
  865.             case kUngroup:
  866.                 DoUngroup(frHndl);
  867.                 break;
  868.         }
  869.     }
  870.     else {
  871.         root = (*frHndl)->d.doc.root;
  872.  
  873.         cnum = 0;
  874.         cdir = 1;
  875.         cend = (*root)->numChildren;
  876.  
  877.         if (cend)
  878.             NewDocumentUndo(frHndl);
  879.  
  880.         if (menuItem >= kMoveBackward) {
  881.             cnum = cend - 1;
  882.             cdir = -1;
  883.             cend = -1;
  884.         }
  885.  
  886.         for (; cnum != cend; cnum += cdir) {
  887.             if (DoTreeObjMethod(GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  888.                 switch (menuItem) {
  889.                     case kMoveForward:
  890.                         if (cnum)
  891.                             MoveChild(MOVEFORWARD_EDIT, root, cnum, root, cnum - 1);
  892.                         break;
  893.                     case kMoveToFront:
  894.                         MoveChild(MOVETOFRONT_EDIT, root, cnum, root, 0);
  895.                         break;
  896.                     case kMoveBackward:
  897.                         MoveChild(MOVEBACKWARD_EDIT, root, cnum, root, cnum + 1);
  898.                         break;
  899.                     case kMoveToBack:
  900.                         MoveChild(MOVETOBACK_EDIT, root, cnum, root, -1);
  901.                         break;
  902.                 }
  903.             }
  904.         }
  905.     }
  906.     SetWindowDirty(window = (*frHndl)->fileState.window);
  907.  
  908.     BeginContent(window);
  909.     DoImageDocument(frHndl);
  910.     EndContent(window);
  911. }
  912.  
  913.  
  914.  
  915. /*****************************************************************************/
  916.  
  917.  
  918.  
  919. Rect    GetSelectedArea(TreeObjHndl root)
  920. {
  921.     TreeObjHndl    child;
  922.     short        cnum;
  923.     Rect        selectRct, rct;
  924.     Boolean        first;
  925.  
  926.     SetRect(&selectRct, 0, 0, 0, 0);
  927.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  928.         child = GetChildHndl(root, --cnum);
  929.         if (DoTreeObjMethod(child, GETSELECTMESSAGE, 0)) {
  930.             DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  931.             if (!EmptyRect(&rct)) {
  932.                 if (first) {
  933.                     selectRct = rct;
  934.                     first = false;
  935.                 }
  936.                 else UnionRect(&selectRct, &rct, &selectRct);
  937.             }
  938.         }
  939.     }
  940.     return(selectRct);
  941. }    
  942.  
  943.  
  944.  
  945. /*****************************************************************************/
  946.  
  947.  
  948.  
  949. Rect    GetDataArea(TreeObjHndl root)
  950. {
  951.     TreeObjHndl    child;
  952.     short        cnum;
  953.     Rect        areaRct, rct;
  954.     Boolean        first;
  955.  
  956.     SetRect(&areaRct, 0, 0, 0, 0);
  957.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  958.         child = GetChildHndl(root, --cnum);
  959.         DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  960.         if (!EmptyRect(&rct)) {
  961.             if (first) {
  962.                 areaRct = rct;
  963.                 first   = false;
  964.             }
  965.             else UnionRect(&areaRct, &rct, &areaRct);
  966.         }
  967.     }
  968.     return(areaRct);
  969. }    
  970.  
  971.  
  972.  
  973. /*****************************************************************************/
  974.  
  975.  
  976.  
  977. void    SetDataArea(TreeObjHndl root, short h, short v)
  978. {
  979.     if (h != kwNoChange) {
  980.         h /= (7 * 72);
  981.         ++h;
  982.         h *= (7 * 72);
  983.     }
  984.  
  985.     if (v != kwNoChange) {
  986.         v /= (10 * 72);
  987.         ++v;
  988.         v *= (10 * 72);
  989.     }
  990.  
  991.     SetDocSize(mDerefRoot(root)->frHndl, h, v);
  992. }    
  993.  
  994.  
  995.  
  996. /*****************************************************************************/
  997.  
  998.  
  999.  
  1000. void    DoGroup(FileRecHndl frHndl)
  1001. {
  1002.     TreeObjHndl    root, group, child;
  1003.     short        gnum, cnum;
  1004.     Rect        groupRct;
  1005.  
  1006.     NewDocumentUndo(frHndl);
  1007.  
  1008.     root = (*frHndl)->d.doc.root;
  1009.     for (gnum = 0; gnum < (*root)->numChildren; ++gnum)
  1010.         if (DoTreeObjMethod(GetChildHndl(root, gnum), GETSELECTMESSAGE, 0)) break;
  1011.  
  1012.     if (!(group = NewChild(GROUP_EDIT, root, gnum, GROUPOBJ, 0))) return;
  1013.  
  1014.     groupRct = GetSelectedArea(root);
  1015.     for (cnum = (*root)->numChildren - 1; cnum > gnum; --cnum) {
  1016.         if (DoTreeObjMethod(child = GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  1017.             DoTreeObjMethodClipped(child, SETSELECTMESSAGE, SELECTOFF);
  1018.             MoveChild(GROUP_EDIT, root, cnum, group, 0);
  1019.         }
  1020.     }
  1021.  
  1022.     mDerefGroup(group)->group = groupRct;
  1023. }    
  1024.  
  1025.  
  1026.  
  1027. /*****************************************************************************/
  1028.  
  1029.  
  1030.  
  1031. void    DoUngroup(FileRecHndl frHndl)
  1032. {
  1033.     TreeObjHndl    root, group;
  1034.     short        gnum, cnum;
  1035.  
  1036.     NewDocumentUndo(frHndl);
  1037.  
  1038.     root = (*frHndl)->d.doc.root;
  1039.     for (gnum = (*root)->numChildren; gnum;) {
  1040.         if (DoTreeObjMethod(group = GetChildHndl(root, --gnum), GETSELECTMESSAGE, 0)) {
  1041.             if ((*group)->type == GROUPOBJ) {
  1042.                 DoTreeSelect(group, SELECTOFF);
  1043.                 for (cnum = (*group)->numChildren; cnum;) {
  1044.                     MoveChild(UNGROUP_EDIT, group, --cnum, root, gnum + 1);
  1045.                     DoTreeSelect(GetChildHndl(root, gnum + 1), SELECTON);
  1046.                 }
  1047.                 DisposeChild(UNGROUP_EDIT, root, gnum);
  1048.             }
  1049.         }
  1050.     }
  1051. }    
  1052.  
  1053.  
  1054.  
  1055. /*****************************************************************************/
  1056. /*****************************************************************************/
  1057. /*****************************************************************************/
  1058.  
  1059.  
  1060.  
  1061. void    AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click)
  1062. {
  1063.     TreeObjHndl        root, cobj, origHndl, oldHndl;
  1064.     WindowPtr        window;
  1065.     short            cnum, h, w, i;
  1066.     short            adding, extSelect;
  1067.     LayerObj        wlyr, wklyr, blyr;
  1068.     Rect            oldRct, newRct, rct, extRct1, extRct2, extRct3;
  1069.     OSErr            err;
  1070.     LayerDrawInfo    drawInfo;
  1071.     Boolean            tool, keepTool, newTool;
  1072.     Point            curMouse, org, oldOrg;
  1073.  
  1074.     root   = (*frHndl)->d.doc.root;
  1075.     window = (*frHndl)->fileState.window;
  1076.  
  1077.     tool      = GetTool();
  1078.     keepTool  = GetToolPersistence();
  1079.     adding    = false;
  1080.     extSelect = false;
  1081.     newTool   = false;
  1082.  
  1083.     if (!hndl) {        /* If hndl == nil, then we are adding a new object. */
  1084.         adding = true;
  1085.         if (!tool)
  1086.             extSelect = true;
  1087.         else
  1088.             DoTreeSelect(root, SELECTOFF);        /* Turn off all old selections. */
  1089.         click->offset.h = 0;
  1090.         click->offset.v = 0;
  1091.         click->grabber  = 0;
  1092.         click->oldFlip  = 0;
  1093.         click->newFlip  = 0;
  1094.  
  1095.         i = (extSelect) ? EXTSELECTOBJ : ((RECTOBJ - 1) + tool);
  1096.         hndl = NewChild(NO_EDIT, root, cnum = 0, i, 0);
  1097.             /* The child gets created selected, ready to go, except that it's rect is
  1098.             ** empty.  As the user drags the object out, this will change. */
  1099.  
  1100.         if (!keepTool) {
  1101.             tool    = 0;
  1102.             newTool = true;
  1103.         }            /* If the tool is a one-use tool, dispose it. */
  1104.  
  1105.         if (hndl) {        /* If we succeeded at creating something... */
  1106.             rct.top  = rct.bottom = click->localEvent.where.v;
  1107.             rct.left = rct.right  = click->localEvent.where.h;
  1108.             DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);
  1109.             DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1110.         }
  1111.     }
  1112.     else {
  1113.         NewDocumentUndo(frHndl);
  1114.         cnum = GetChildNum(hndl);
  1115.         if (ModifyChild(SIZE_EDIT, root, cnum, false)) return;
  1116.             /* Out of memory.  I would handle this case better if it weren't just sample code.
  1117.             ** Given that there are no complex objects, we actually can't run out of memory
  1118.             ** due to this operation.  However, there may someday be a pixmap type of object,
  1119.             ** whose memory hit could be substantial. */
  1120.     }
  1121.  
  1122.     drawInfo.root = root;        /* The tree we are operating with. */
  1123.     drawInfo.cnum = cnum;        /* The backmost object that is changing. */
  1124.  
  1125.     if (hndl) {
  1126.         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1127.         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1128.         if (!err)
  1129.             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1130.         if (err) DisposeThisAndBelowLayers(wklyr);
  1131.             /* The above code creates the necessary offscreen layers for the following editing.
  1132.             ** The window layer is bound to succeed at getting created, as it uses the window,
  1133.             ** instead of creating an offscreen GWorld.  The work layer and back layer creations
  1134.             ** may fail.  If they do, then we will only have a window layer.  The window layer
  1135.             ** is smart enough to notice that it is alone, and if it is, then it will do the
  1136.             ** drawing directly to the window.  Drawing directly to the window means that there
  1137.             ** will be a bunch of flicker, but what else is there to do when there is not enough
  1138.             ** ram?  Drawing nothing is even worse. */
  1139.  
  1140.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1141.             /* Bounding rect of the object getting created or resized. */
  1142.  
  1143.         if (click->grabber)
  1144.             InsetRect(&rct, -3, -3);
  1145.         InvalLayer(wlyr, rct, true);
  1146.             /* If getting resized, then the object has grabbers, and we have to make sure
  1147.             ** that the area getting redrawn is large enough for the grabbers. */
  1148.  
  1149.         origHndl = hndl;
  1150.         HandToHand((Handle *)&origHndl);
  1151.         while (StillDown()) {
  1152.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  1153.             oldHndl = hndl;
  1154.             HandToHand((Handle *)&oldHndl);
  1155.             if (DoTreeObjMethod(hndl, SIZEMESSAGE, (long)click)) {        /* If new size... */
  1156.                 DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&newRct);
  1157.                 if (!err) {        /* If we have all offscreen layers... */
  1158.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1159.                     if (click->grabber)
  1160.                         InsetRect(&rct, -3, -3);
  1161.                     InvalLayer(wlyr, rct, true);
  1162.                     UpdateLayer(wlyr);
  1163.                 }
  1164.                 else {            /* If we only have wlyr... */
  1165.                     SwapTreeObjData(hndl, oldHndl);
  1166.                     if (!EqualTreeObjData(hndl, origHndl))
  1167.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1168.                     SwapTreeObjData(hndl, oldHndl);
  1169.                     if (!EqualTreeObjData(hndl, origHndl))
  1170.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1171.                 }
  1172.             }
  1173.             DisposeHandle((Handle)oldHndl);
  1174.  
  1175.             GetMouse(&curMouse);
  1176.             GetContentRect(window, &rct);
  1177.             if (!PtInRect(curMouse, &rct)) {
  1178.  
  1179.                 GetContentOrigin(window, &org);
  1180.                 oldOrg = org;
  1181.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1182.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1183.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1184.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1185.  
  1186.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1187.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1188.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1189.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1190.  
  1191.                 rct = GetDataArea(root);
  1192.                 if (rct.right < curMouse.h)
  1193.                     rct.right = curMouse.h;
  1194.                 if (rct.bottom < curMouse.v)
  1195.                     rct.bottom = curMouse.v;
  1196.                 SetDataArea(root, rct.right, rct.bottom);
  1197.  
  1198.                 SetContentOrigin(window, org.h, org.v);
  1199.                 GetContentOrigin(window, &org);
  1200.  
  1201.                 if (!err) {
  1202.                     if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1203.                         DisposeThisAndBelowLayers(wlyr);
  1204.                         DoImageDocument(frHndl);
  1205.                         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1206.                         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1207.                         if (!err)
  1208.                             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1209.                         if (err) DisposeThisAndBelowLayers(wklyr);
  1210.                         GetContentRect(window, &rct);
  1211.                         InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1212.                     }
  1213.                 }
  1214.                 else
  1215.                     DoImageDocument(frHndl);
  1216.             }
  1217.             else {
  1218.                 rct = GetDataArea(root);
  1219.                 SetDataArea(root, rct.right, rct.bottom);
  1220.             }
  1221.         }
  1222.         if (origHndl)
  1223.             DisposeHandle((Handle)origHndl);
  1224.  
  1225.         rct = GetDataArea(root);
  1226.         SetDataArea(root, rct.right, rct.bottom);
  1227.  
  1228.         DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&rct);
  1229.         h = rct.bottom - rct.top;
  1230.         w = rct.right  - rct.left;
  1231.             /* This is how big the final product is.  It may now be unacceptably small.
  1232.             ** If we were adding a new object and it is too small, get rid of it entirely
  1233.             ** and use this to deselect the tool.  If we were resizing an existing object,
  1234.             ** then force the object to be a minimum size. */
  1235.  
  1236.         if (((!h) || (!w)) || ((h < 8) && (w < 8))) {    /* If smaller than minimum... */
  1237.             if (adding) {
  1238.                 DisposeChild(NO_EDIT, root, 0);        /* Don't create a new object that's too small. */
  1239.                 hndl = nil;                    /* We didn't change anything, so flag this situation.  */
  1240.                 SetPaletteTool(0);
  1241.                 newTool = false;
  1242.             }
  1243.             else {
  1244.                 rct.bottom = rct.top  + 8;                /* Don't let them shrink it too small. */
  1245.                 rct.right  = rct.left + 8;
  1246.                 DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);    /* Make it minimum. */
  1247.             }
  1248.             DoImageDocument(frHndl);
  1249.         }
  1250.  
  1251.         if (hndl) {        /* If we added or changed something... */
  1252.  
  1253.  
  1254.             if (adding) {        /* If we are adding, we haven't handled posting the addition
  1255.                                 ** for undo.  The reason for this is that the user may not
  1256.                                 ** actually add an object.  If they just click and release,
  1257.                                 ** there is no object added.  If we add the child with undo
  1258.                                 ** posting, and then remove it if the user doesn't grow it
  1259.                                 ** out, then we may lose some old undo info.  If the user
  1260.                                 ** doesn't grow out the object, the operation is a NOP, and
  1261.                                 ** so old undo information shouldn't be lost. */
  1262.                 if (extSelect) {                            /* We only created the object for extend-select purposes. */
  1263.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&extRct1);
  1264.                     for (i = (*root)->numChildren - 1; i; --i) {
  1265.                         cobj = GetChildHndl(root, i);
  1266.                         DoTreeObjMethod(cobj, GETBBOXMESSAGE, (long)&extRct2);
  1267.                         SectRect(&extRct1, &extRct2, &extRct3);
  1268.                         if (EqualRect(&extRct2, &extRct3))
  1269.                             DoTreeObjMethodClipped(cobj, SETSELECTMESSAGE, SELECTTOGGLE);
  1270.                     }
  1271.                     DisposeChild(NO_EDIT, root, 0);            /* We're done with it, so kill it. */
  1272.                 }
  1273.                 else {
  1274.                     NewDocumentUndo(frHndl);
  1275.                     hndl = CopyChild(NEW_EDIT, root, 0, root, 0, false);
  1276.                     DisposeChild(NO_EDIT, root, 1);
  1277.                 }    /* We just created a posted copy of the child, just as if we had called
  1278.                     ** NewChild.  We also disposed of the temporary unposted copy of the child.
  1279.                     ** The only problem is that objects entering the document get flagged as
  1280.                     ** selected.  This means that when we select the object, nothing happens
  1281.                     ** on the screen because the object thinks it is already selected. */
  1282.                 if (!extSelect)
  1283.                     if (hndl)
  1284.                         DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1285.                             /* Now the select grabbers can be drawn. */
  1286.             }
  1287.  
  1288.             rct = GetDataArea(root);
  1289.             SetDataArea(root, rct.right, rct.bottom);
  1290.  
  1291.             SetWindowDirty(window);
  1292.             if (err)
  1293.                 DoImageDocument(frHndl);
  1294.                     /* If we have only a wlyr, then we haven't really been generating a
  1295.                     ** complete image.  Redraw the document so that a full image is shown. */
  1296.  
  1297.             if (!extSelect)
  1298.                 DoTreeObjMethodClipped(hndl, SETSELECTMESSAGE, SELECTON);
  1299.                     /* Make sure the object is selected. */
  1300.         }
  1301.  
  1302.         DisposeThisAndBelowLayers(wlyr);        /* Clean up. */
  1303.     }
  1304.  
  1305.     if (extSelect)
  1306.         DoImageDocument(frHndl);
  1307.             /* Since we have killed the extend-select object, erase it from the screen. */
  1308.  
  1309.     if (newTool)
  1310.         SetPaletteTool(tool);
  1311. }
  1312.  
  1313.  
  1314.  
  1315. /*****************************************************************************/
  1316.  
  1317.  
  1318.  
  1319. static OSErr    WindowLayerProc(LayerObj theLayer, short message)
  1320. {
  1321.     OSErr            err;
  1322.     WindowPtr        oldPort, window;
  1323.     Point            contOrg;
  1324.     Rect            contRct, thisUpdate;
  1325.     LayerDrawInfo    drawInfo;
  1326.     RgnHandle        oldClip, newClip;
  1327.  
  1328.     err = noErr;
  1329.  
  1330.     drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1331.  
  1332.     switch (message) {
  1333.         case kLayerInit:
  1334.             err = DefaultLayerProc(theLayer, message);
  1335.             GetPort(&oldPort);
  1336.             SetPort(window = (*theLayer)->layerPort);
  1337.             GetContentOrigin(window, &contOrg);
  1338.             SetOrigin(contOrg.h, contOrg.v);
  1339.             GetContentRect(window, &contRct);
  1340.             (*theLayer)->dstRect = contRct;
  1341.                 /* The above calculates the content rect (less tool palette and scrollbars).
  1342.                 ** By setting the dstRect to this smaller rect, work layer and back layer
  1343.                 ** will be created the size of dstRect, instead of the portRect of the
  1344.                 ** window.  This in turn will keep the GWLayers code from drawing over
  1345.                 ** the tool palette and scrollbars. */
  1346.             SetPort(oldPort);
  1347.             break;
  1348.         case kLayerUpdate:
  1349.             BeginContent((*theLayer)->layerPort);
  1350.             if ((*theLayer)->belowLayer)
  1351.                 DefaultLayerProc(theLayer, message);
  1352.             else {        /* If offscreen layers couldn't be made, then do it by hand. */
  1353.                 thisUpdate = UpdateUpdateRects(theLayer);
  1354.                 oldClip = NewRgn();
  1355.                 newClip = NewRgn();
  1356.                 RectRgn(newClip, &thisUpdate);
  1357.                 GetClip(oldClip);
  1358.                 SetClip(newClip);
  1359.                 EraseRect(&thisUpdate);
  1360.                 DoTreeDraw(drawInfo.root, DRAWOBJ);
  1361.                 DoTreeDraw(drawInfo.root, DRAWSELECT);
  1362.                 SetClip(oldClip);
  1363.                 DisposeRgn(oldClip);
  1364.                 DisposeRgn(newClip);
  1365.                 DrawPageGrid();
  1366.             }
  1367.             EndContent((*theLayer)->layerPort);
  1368.             break;
  1369.  
  1370.         default:
  1371.             err = DefaultLayerProc(theLayer, message);
  1372.                 /* Default behavior for everything else. */
  1373.             break;
  1374.     }
  1375.  
  1376.     return(err);
  1377. }
  1378.  
  1379.  
  1380.  
  1381. /*****************************************************************************/
  1382.  
  1383.  
  1384.  
  1385. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1386. {
  1387.     OSErr            err;
  1388.     Rect            rct;
  1389.     LayerDrawInfo    drawInfo;
  1390.     short            cnum;
  1391.     TreeObjHndl        chndl;
  1392.  
  1393.     err = noErr;
  1394.  
  1395.     switch (message) {
  1396.         case kLayerInit:
  1397.             err = DefaultLayerProc(theLayer, message);
  1398.             break;
  1399.         case kLayerUpdate:
  1400.             SetLayerWorld(theLayer);
  1401.             drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1402.             if ((*theLayer)->belowLayer)
  1403.                 DefaultLayerProc(theLayer, message);
  1404.             else {
  1405.                 rct = GetEffectiveDstRect(theLayer);
  1406.                 EraseRect(&rct);
  1407.                 drawInfo.cnum = (*drawInfo.root)->numChildren - 1;
  1408.             }
  1409.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1410.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1411.                 DoTreeDraw(chndl, DRAWOBJ);
  1412.             }
  1413.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1414.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1415.                 DoTreeDraw(chndl, DRAWSELECT);
  1416.             }
  1417.             DrawPageGrid();
  1418.             ResetLayerWorld(theLayer);
  1419.             break;
  1420.         default:
  1421.             err = DefaultLayerProc(theLayer, message);
  1422.                 /* Default behavior for everything else. */
  1423.             break;
  1424.     }
  1425.  
  1426.     return(err);
  1427. }
  1428.  
  1429.  
  1430.  
  1431. /*****************************************************************************/
  1432.  
  1433.  
  1434.  
  1435. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1436. {
  1437.     OSErr            err;
  1438.     Rect            rct;
  1439.     LayerDrawInfo    drawInfo;
  1440.     short            cnum;
  1441.     TreeObjHndl        chndl;
  1442.  
  1443.     err = noErr;
  1444.  
  1445.     switch (message) {
  1446.         case kLayerInit:
  1447.             err = DefaultLayerProc(theLayer, message);
  1448.             if (!err) {
  1449.                 SetLayerWorld(theLayer);
  1450.                 rct = GetEffectiveDstRect(theLayer);
  1451.                 EraseRect(&rct);
  1452.                 drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1453.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1454.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1455.                     DoTreeDraw(chndl, DRAWOBJ);
  1456.                 }
  1457.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1458.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1459.                     DoTreeDraw(chndl, DRAWSELECT);
  1460.                 }
  1461.                 ResetLayerWorld(theLayer);
  1462.             }
  1463.             break;
  1464.         default:
  1465.             err = DefaultLayerProc(theLayer, message);
  1466.                 /* Default behavior for everything else. */
  1467.             break;
  1468.     }
  1469.  
  1470.     return(err);
  1471. }
  1472.  
  1473.  
  1474.  
  1475. /*****************************************************************************/
  1476.  
  1477.  
  1478.  
  1479. void    DrawPageGrid(void)
  1480. {
  1481.     WindowPtr    curPort;
  1482.     short        x1, x2, y1, y2, i;
  1483.  
  1484.     if (!gPrintPage) {
  1485.         GetPort(&curPort);
  1486.         PenPat((ConstPatternParam)&qd.gray);
  1487.         x1  = curPort->portRect.left   / (7 * 72);
  1488.         x1 *= (7 * 72);
  1489.         x2  = curPort->portRect.right  / (7 * 72);
  1490.         x2 *= (7 * 72);
  1491.         y1  = curPort->portRect.top    / (10 * 72);
  1492.         y1 *= (10 * 72);
  1493.         y2  = curPort->portRect.bottom / (10 * 72);
  1494.         y2 *= (10 * 72);
  1495.         for (i = x1; i <= x2; i += (7 * 72)) {
  1496.             MoveTo(i - 1, -1);
  1497.             Line  (0, 16383);
  1498.         }
  1499.         for (i = y1; i <= y2; i += (10 * 72)) {
  1500.             MoveTo(-1, i - 1);
  1501.             Line  (16383, 0);
  1502.         }
  1503.         PenNormal();
  1504.     }
  1505. }
  1506.  
  1507.  
  1508.  
  1509. /*****************************************************************************/
  1510.  
  1511.  
  1512.  
  1513. void    SlideSelection(FileRecHndl frHndl, ClickInfo *click)
  1514. {
  1515.     LayerDrawInfo    drawInfo;
  1516.     WindowPtr        window;
  1517.     LayerObj        wlyr, wklyr, blyr;
  1518.     Point            lastMouse, curMouse;
  1519.     short            cnum, dh, dv;
  1520.     TreeObjHndl        root, chndl;
  1521.     Rect            newLoc, selectRct, rct;
  1522.     Point            oldOrg, org;
  1523.     Boolean            changed, autoScroll, dragDrop;
  1524.     OSErr            err;
  1525.     EventRecord        event;
  1526.     long            as, autoScrollTick;
  1527.  
  1528.     autoScrollTick = 0;
  1529.  
  1530.     changed = false;
  1531.     NewDocumentUndo(frHndl);
  1532.  
  1533.     root = (*frHndl)->d.doc.root;
  1534.     for (cnum = (*root)->numChildren;;) {
  1535.         if (DoTreeObjMethod(GetChildHndl(root, --cnum), GETSELECTMESSAGE, 0)) break;
  1536.         if (!cnum) return;
  1537.     }
  1538.  
  1539.     drawInfo.root = root;
  1540.     drawInfo.cnum = cnum;
  1541.     window        = (*frHndl)->fileState.window;
  1542.  
  1543.                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1544.               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1545.     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1546.  
  1547.     selectRct = GetSelectedArea(root);
  1548.     InsetRect(&selectRct, -3, -3);
  1549.     InvalLayer(wlyr, selectRct, false);
  1550.     lastMouse = click->localEvent.where;
  1551.  
  1552.     autoScroll = dragDrop = false;
  1553.     while (StillDown()) {
  1554.         GetMouse(&curMouse);
  1555.         GetContentRect(window, &rct);
  1556.         rct.top  += 16;
  1557.         rct.left += 16;
  1558.         as = autoScroll;
  1559.         autoScroll = !PtInRect(curMouse, &rct);
  1560.         if (autoScroll) {
  1561.             if (!autoScrollTick) autoScrollTick = TickCount();
  1562.             dh = lastMouse.h - curMouse.h;
  1563.             if (dh < 0) dh = -dh;
  1564.             dv = lastMouse.v - curMouse.v;
  1565.             if (dv < 0) dv = -dv;
  1566.             if ((dh + dv > 6) && (!as)) autoScrollTick = TickCount();
  1567.             if (autoScrollTick + 15 > TickCount()) autoScroll = false;
  1568.         }
  1569.         if (DragDropAvailable()) {
  1570.             InsetRect(&rct, -16, -16);
  1571.             dragDrop = !PtInRect(curMouse, &rct);
  1572.             InsetRect(&rct,  16,  16);
  1573.         }
  1574.  
  1575.         if ((dragDrop) || (click->localEvent.modifiers & optionKey)) {
  1576.             if (changed) RevertEdit(root, true);
  1577.             event = click->localEvent;
  1578.             LocalToGlobal(&event.where);
  1579.             if (DoDrag(frHndl, &event) != paramErr) break;
  1580.         }
  1581.  
  1582.         click->offset.h = (curMouse.h - lastMouse.h);
  1583.         click->offset.v = (curMouse.v - lastMouse.v);
  1584.         click->message  = CLICKDRAG;
  1585.  
  1586.         if ((autoScroll) || (click->offset.h) || (click->offset.v)) {
  1587.             changed = true;
  1588.             if ((click->offset.h) || (click->offset.v)) {
  1589.                 for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  1590.                     chndl = GetChildHndl(root, cnum);
  1591.                     if (mDerefCommon(chndl)->selected) {
  1592.                         ModifyChild(MOVE_EDIT, root, cnum, true);
  1593.                         DoFTreeMethod(chndl, CLICKMESSAGE, (long)click);
  1594.                         DoTreeObjMethod(chndl, GETBBOXMESSAGE, (long)&newLoc);
  1595.                         InsetRect(&newLoc, -3, -3);
  1596.                         InvalLayer(wlyr, newLoc, true);
  1597.                     }
  1598.                 }
  1599.                 UpdateLayer(wlyr);
  1600.             }
  1601.             lastMouse = curMouse;
  1602.  
  1603.             if (autoScroll) {
  1604.                 GetContentOrigin(window, &org);
  1605.                 oldOrg = org;
  1606.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1607.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1608.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1609.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1610.  
  1611.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1612.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1613.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1614.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1615.  
  1616.                 rct = GetDataArea(root);
  1617.                 if (rct.right < curMouse.h)
  1618.                     rct.right = curMouse.h;
  1619.                 if (rct.bottom < curMouse.v)
  1620.                     rct.bottom = curMouse.v;
  1621.                 SetDataArea(root, rct.right, rct.bottom);
  1622.  
  1623.                 SetContentOrigin(window, org.h, org.v);
  1624.                 GetContentOrigin(window, &org);
  1625.                 if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1626.                     DisposeThisAndBelowLayers(wlyr);
  1627.                     DoImageDocument(frHndl);
  1628.                                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1629.                               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1630.                     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1631.                     if (err) DisposeThisAndBelowLayers(wklyr);
  1632.                     GetContentRect(window, &rct);
  1633.                     InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1634.                 }
  1635.             }
  1636.             else {
  1637.                 rct = GetDataArea(root);
  1638.                 SetDataArea(root, rct.right, rct.bottom);
  1639.             }
  1640.         }
  1641.     }
  1642.  
  1643.     rct = GetDataArea(root);
  1644.     SetDataArea(root, rct.right, rct.bottom);
  1645.  
  1646.     if (changed)
  1647.         SetWindowDirty(window);
  1648.  
  1649.     DisposeThisAndBelowLayers(wlyr);
  1650. }
  1651.  
  1652.  
  1653.  
  1654. /*****************************************************************************/
  1655.  
  1656.  
  1657.  
  1658. void    NewDocumentUndo(FileRecHndl frHndl)
  1659. {
  1660.     NewUndo((*frHndl)->d.doc.root);
  1661. }
  1662.  
  1663.  
  1664.  
  1665.